package com.nhncorp.pleiades.client;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class LockEventFilter implements EventFilter{
  
  private List<LockService.Events> lockEventList;
  private Set<Integer> blankEventSet = new TreeSet<Integer>();
  private Map<EventFilter.Operator, String> constraints;
  private String keyword;
  
  public static final Log LOG = LogFactory.getLog(LockEventFilter.class
      .getName());
  
  public LockEventFilter() {
    this.keyword = "";
    initDefaultConstraint(keyword);
    initBlankEventSet();
  }
  
  public LockEventFilter(String keyword) {
    this.keyword = keyword;
    initDefaultConstraint(keyword);
    initBlankEventSet();
  }
  
  private void initDefaultConstraint(String defaultKeyword) {
    lockEventList = new ArrayList<LockService.Events>();
    for(LockService.Events entry : LockService.Events.values()) {
      lockEventList.add(entry);
    }
    constraints = new ConcurrentHashMap<EventFilter.Operator, String>();
    constraints.put(EventFilter.Operator.ANY, defaultKeyword);
  }
  
  private void initBlankEventSet() {
    blankEventSet.add(LockService.Events.CLIENT_EXPIRED.ordinal());
    blankEventSet.add(LockService.Events.MASTER_FAILOVER.ordinal());
    blankEventSet.add(LockService.Events.SESSION_EXPIRED.ordinal());
    blankEventSet.add(LockService.Events.SESSION_SAFE.ordinal());
    blankEventSet.add(LockService.Events.SESSION_WARNING.ordinal());
  }
  
  public void addConstraint(EventFilter.Operator operator, String key) {
    constraints.put(operator, key);
  }
  
  public void addConstraint(Map<EventFilter.Operator, String> constraintMap) {
    constraints.putAll(constraintMap);
  }
  
  public void addConstraint(Constraint con) {
    constraints.putAll(con.getConstraints());
  }
  
  public void removeConstraint(EventFilter.Operator operator) {
    constraints.remove(operator);
  }
  
  public void removeConstraint(Constraint con) {
    Map<EventFilter.Operator, String> copiedConstraints = new HashMap<EventFilter.Operator, String>();    
    synchronized (constraints) {
      copiedConstraints.putAll(constraints);
      for(Map.Entry<EventFilter.Operator, String> entry : copiedConstraints.entrySet()) {
        constraints.remove(entry);
      }
    }
  }
  
  public void removeAll() {
    constraints.clear();
    constraints.put(EventFilter.Operator.ANY, keyword);
  }
  
  public String getConstraintKeyword(EventFilter.Operator operator) {
    return constraints.get(operator);
  }
  
  public Map<EventFilter.Operator, String> getAllConstraints() {
    return this.constraints;
  }
  
  public boolean doFilter(LockEventData eventData) throws IOException{
    boolean matched = true;    
    Operator op;
    if( !blankEventSet.contains(eventData.getEventType())) {
      for(Map.Entry<EventFilter.Operator, String> entry : constraints.entrySet()) {
        op = entry.getKey();
        if(op == Operator.EQ) {
          if(!eventData.getLockId().equals(entry.getValue())) {
            matched = false;
            break;
          }
        } else if(op == Operator.PF) {
          if(!eventData.getLockId().startsWith(entry.getValue())) {
            matched = false;
            break;
          }
        } else if(op == Operator.SF) {
          if(!eventData.getLockId().endsWith(entry.getValue())) {
            matched = false;
            break;
          }
        } else if(op == Operator.SS) {
          if(eventData.getLockId().indexOf(entry.getValue()) < 0) {
            matched = false;
            break;
          }
        } else if(op == Operator.LT) {
          if(eventData.getLockId().compareToIgnoreCase(entry.getValue()) >= 0 ) {
            matched = false;
            break;
          }
        } else if(op == Operator.GT) {
          if(eventData.getLockId().compareToIgnoreCase(entry.getValue()) <= 0 ) {
            matched = false;
            break;
          }
        } else if(op == Operator.LE) {
          if(eventData.getLockId().compareToIgnoreCase(entry.getValue()) > 0 ) {
            matched = false;
            break;
          }
        } else if(op == Operator.GE) {
          if(eventData.getLockId().compareToIgnoreCase(entry.getValue()) < 0 ) {
            matched = false;
            break;
          }
        } else if(op == Operator.NE) {
          if(eventData.getLockId().equals(entry.getValue())) {
            matched = false;
            break;
          }
        } else if(op == Operator.ANY) {
          continue;
        } else {
          LOG.fatal("Wrong Operator");
          throw new IOException("Wrong Operator");
        }
      }
    }
    if(matched == true && lockEventList != null && !lockEventList.isEmpty()) {
      matched = false;
      for(LockService.Events entry : lockEventList) {
        if(eventData.getEventType() == entry.ordinal()) {
          matched = true;
          LOG.debug("passed LockEventFilter: " + eventData.getLockId() + ", "
              + eventData.getEventType() + "," + entry.ordinal());
          break;
        }
      }
    }         
    
    return matched;
  }

  public String getKeyword() {
    return keyword;
  }

  public void setKeyword(String keyword) {
    this.keyword = keyword;
  }

  public List<LockService.Events> getLockEventList() {
    return this.lockEventList;
  }

  public void setLockEventList(List<LockService.Events> lockEventList) {
    this.lockEventList = lockEventList;
  }

}
